package com.tbit.uqbike.client.service.event.impl;

import cn.hutool.core.util.StrUtil;
import com.tbit.uqbike.client.dao.core.EventFilterConfigDao;
import com.tbit.uqbike.client.pojo.event.EventFilterConfigDO;
import com.tbit.uqbike.client.service.event.EventFilterConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 事件埋点过滤配置 Service 实现类
 *
 * @author: qiu.longjie
 * @create: 2025-06-03
 */
@Service
@Slf4j
public class EventFilterConfigServiceImpl implements EventFilterConfigService {

    // 使用 AtomicReference 来持有配置 Map 的引用，确保原子性更新
    // 初始设置为空Map，防止NPE，同时默认是只读的
    private final AtomicReference<Map<String, EventFilterConfigDO>> configCache = new AtomicReference<>(Collections.emptyMap());

    @Autowired
    private EventFilterConfigDao eventFilterConfigDao;

    @Override
    public List<EventFilterConfigDO> listValidConfigs() {
        return eventFilterConfigDao.selectValidConfigs();
    }

    @Override
    public EventFilterConfigDO getByUri(String uri) {
        if (StrUtil.isEmpty(uri)) {
            return null;
        }
        // 直接从当前缓存引用中获取
        // get() 方法是线程安全的，它返回的是当前的Map引用
        return configCache.get().get(uri);
    }

    /**
     * 定时任务和应用启动时调用此方法，全量加载并刷新 JVM 内存缓存。
     */
    @Scheduled(cron = "0 30 3 * * ?") // 每天凌晨 3:30 执行
    public void refreshAllConfigsToCache() {
        log.info("开始定时任务：全量刷新事件埋点过滤配置 JVM 内存缓存...");
        try {
            // 1. 从数据库加载所有有效配置
            List<EventFilterConfigDO> configs = listValidConfigs();

            Map<String, EventFilterConfigDO> newConfigMap;
            if (CollectionUtils.isEmpty(configs)) {
                newConfigMap = Collections.emptyMap(); // 如果数据库为空，设置为一个不可变的空Map
                log.warn("数据库中未找到任何有效事件埋点过滤配置。");
            } else {
                // 使用普通的 HashMap 构建新Map，完成后再替换
                newConfigMap = configs.stream()
                        .collect(Collectors.toMap(EventFilterConfigDO::getUri, Function.identity(), (v1, v2) -> v1));
                log.info("从数据库加载到 {} 条事件埋点过滤配置。", configs.size());
            }

            // 2. 原子性地替换旧的缓存引用
            // 这确保了在替换过程中，读取操作始终能够获取到完整的旧Map或完整的最新Map
            configCache.set(newConfigMap);
            log.info("成功将 {} 条配置刷新到 JVM 内存缓存。", newConfigMap.size());

        } catch (Exception e) {
            log.error("定时任务：全量刷新事件埋点过滤配置 JVM 内存缓存异常！", e);
        }
        log.info("定时任务：事件埋点过滤配置 JVM 内存缓存刷新完成。");
    }

    // 应用程序启动时，立即触发一次缓存加载
    // 配合 @Scheduled 的 initialDelay，确保在定时任务首次执行前，缓存就有数据
    @PostConstruct
    public void initCacheOnStartup() {
        refreshAllConfigsToCache();
    }
}
